﻿using System;
using System.Collections.Generic;
using System.Text;
using AgilePlannerDT.Data;
using System.IO;
using AgilePlannerDT.Data.Impl;
using System.Xml;
using System.Xml.Serialization;

namespace AgilePlannerDT.Persister.Impl.File
{
    public class SyncPersisterToXML : SynchronousPersister
    {
        #region Constants

        public static readonly string DEFAULT_PROJECT_DIRECTORY = "ProjectDirectory";
        public static readonly string DEFAULT_PROJECT_NAME = "ProjectFile";

        protected static readonly string PROJECT_FILE_EXTENSION = ".xml";
        protected static readonly string HANDWRITING_FILE_EXTENSION = ".jpg";
        protected static readonly string HANDWRITING_SUBFOLDER_NAME = "Handwriting";

        #endregion

        #region Attributes

        private Project project;

        private FileSystemIDGenerator generator;

        private DirectoryInfo projectDirectory, handwritingDirectory;
        private FileInfo projectFile;

        private string currentProjectName, localProjectDirPath;

        #endregion

        #region Constructors

        public SyncPersisterToXML()
            : this(DEFAULT_PROJECT_DIRECTORY, DEFAULT_PROJECT_NAME)
        {
        }

        public SyncPersisterToXML(string localProjectDirectoryPath, string projectName)
        {
            this.generator = FileSystemIDGenerator.GetInstance();

            this.currentProjectName = projectName;
            this.localProjectDirPath = localProjectDirectoryPath;

            if (!Directory.Exists(localProjectDirectoryPath))
                this.projectDirectory = Directory.CreateDirectory(localProjectDirectoryPath);
            else
                this.projectDirectory = new DirectoryInfo(localProjectDirectoryPath);

            this.handwritingDirectory = new DirectoryInfo(projectDirectory.FullName + "\\" + currentProjectName + "\\" + HANDWRITING_SUBFOLDER_NAME);

            this.projectFile = new FileInfo(projectDirectory + "\\" + projectName + PROJECT_FILE_EXTENSION);

            this.project = new ProjectDataObject();

            if (projectFile.Exists)
                Load();
            else
                CreateProject(projectName);
        }

        #endregion

        #region SynchronousPersister Members

        #region Properties

        public Project Project
        {
            get { return (Project)this.project; }
            set
            {
                this.project = new ProjectDataObject(value);
            }
        }

        #endregion

        #region Load

        public Project Load(string projectName)
        {
  /*          if (project.Name.Equals(projectName))
            {
                return (Project)project.Clone();
            }
            else*/
            {
                projectFile = new FileInfo(projectDirectory.FullName + "\\" + projectName + PROJECT_FILE_EXTENSION);

                if (projectFile.Exists)
                {
                    Load();
                    //TODO load the handwritings if there are any
                }
                else
                {
                    CreateProject(projectName);
                }
            }

         //   return (Project)project.Clone();
            return project;
        }

        public Project Load(string projectName, DateTime start, DateTime end)
        {
            throw new NotImplementedException();
        }

        public List<string> GetProjectNames()
        {
            FileInfo[] files = projectDirectory.GetFiles();

            List<string> prjNames = new List<string>();

            foreach (FileInfo fi in files)
            {
                //string name;

                if (fi.Extension == PROJECT_FILE_EXTENSION)
                {
                    //name = fi.Name.Substring(0, fi.Name.Length - 4);
                    prjNames.Add(Path.GetFileNameWithoutExtension(fi.Name));
                }
            }

            return prjNames;
        }

        #endregion

        #region Create

        public Backlog CreateBacklog(int width, int height, int locationX, int locationY)
        {
            Backlog back = project.CreateBacklog(width, height, locationX, locationY);
            Save();
            return (Backlog)back.Clone();
        }

        public Iteration CreateIteration(string name, string description, int width, int height, int locationX, int locationY, float availableEffort, DateTime startDate, DateTime endDate)
        {
            Iteration iter = project.CreateIteration(name, description, width, height, locationX, locationY, availableEffort, startDate, endDate);
            Save();
            return (Iteration)iter.Clone();
        }

        public Project CreateProject(string name)
        {
            project = new ProjectDataObject(name);
            //generator.Reset();            
            //project.ID = generator.GetNextID();

            //Backlog backlog = new BacklogDataObject(0, 0, 0, 0);
            //backlog.ID = generator.GetNextID();
            //project.Backlog = backlog;
            //project.Legend = new LegendDataObject();

            currentProjectName = project.Name;
            projectFile = new FileInfo(projectDirectory.FullName + "\\" + name + PROJECT_FILE_EXTENSION);

            Save();

            return (Project)project.Clone();
        }

        public StoryCard CreateStoryCard(string name, string description, int width, int height, int locationX, int locationY, long parentid, float bestCaseEstimate, float mostlikelyEstimate, float worstCaseEstimate, float actualEffort, string status, string color, string cardOwner, float rotationAngle,string rallyID, string fitID)
        {
            StoryCard sc = project.CreateStoryCard(name, description, width, height, locationX, locationY, parentid, bestCaseEstimate, mostlikelyEstimate, worstCaseEstimate, actualEffort, status, color, cardOwner, rotationAngle,rallyID,fitID);
            Save();
            return (StoryCard)sc.Clone();
        }

        #endregion

        #region Delete

        public IndexCard DeleteCard(long id)
        {
            IndexCard card = project.DeleteCard(id);
            Save();
            return (IndexCard)card.Clone();
        }

        public bool DeleteProject(string projectName)
        {
            if (currentProjectName.Equals(projectName))
            {
                //CreateProject(projectName);
                projectFile.Delete();
                CreateProject(DEFAULT_PROJECT_NAME);
                return true;
            }
            else
            {

                FileInfo pfi = new FileInfo(projectDirectory.FullName + "\\" + projectName + PROJECT_FILE_EXTENSION);

                if (pfi.Exists)
                {
                    pfi.Delete();
                    return true;
                }
            }

            return false;
        }

        #endregion

        #region Undelete

        public IndexCard UndeleteCard(IndexCard indexCard)
        {
            IndexCard card = project.UndeleteCard(indexCard);
            Save();
            return (IndexCard)card.Clone();
        }

        #endregion

        #region Update

        public IndexCard UpdateCard(IndexCard indexCard)
        {
            IndexCard card = project.UpdateCard(indexCard);

            if (card is StoryCard)
            {
                if (((StoryCard)card).HandwritingImage.Length > 0)
                {
                    SaveHandWritingImage((StoryCard)card);
                }
            }

            Save();
            return (IndexCard)card.Clone();
        }

        public Legend UpdateLegend(Legend legend)
        {
            Legend lgnd = project.UpdateLegend(legend);
            Save();
            return (Legend)lgnd.Clone();
        }


        public void AddNewUser(TeamMember member)
        {
            this.project.AddTeamMember(member);
            Save();
           
        }

        public void RemoveUser(TeamMember member)
        {
            this.project.RemoveTeamMember(member);
            Save();
        }
        #endregion

        #region Move

        public StoryCard MoveStoryCardToNewParent(long id, long oldparentid, long newparentid, int locationX, int locationY, float rotationAngle)
        {
            StoryCard sc = project.MoveStoryCardToNewParent(id, oldparentid, newparentid, locationX, locationY, rotationAngle);
            Save();
            return (StoryCard)sc.Clone();
        }

        #endregion

        #region Find Index Card

        public IndexCard FindCard(long id)
        {
            return (IndexCard)project.FindCard(id).Clone();
        }

        #endregion

        #region Project

        public Project ArrangeProject(Project project2)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Dot Voting

        public void StartVoting()
        {

        }

        #endregion

        #endregion

        #region Helper

        private bool ProjectExists(string projectName)
        {
            FileInfo[] files = projectDirectory.GetFiles(projectName + PROJECT_FILE_EXTENSION);

            if (files != null)
            {
                if (files.Length == 0)
                {
                    //projectFile = files[0];
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Loads the project data from an xml file.
        /// </summary>
        private void Load()
        {
            //maybe this will cause a performance problem
            this.project = new ProjectDataObject();
            FileStream fs = null;

            try
            {
                //XmlSerializer serializer = new XmlSerializer(project.GetType());
                fs = projectFile.OpenRead();
                XmlTextReader reader = new XmlTextReader(fs);
                reader.WhitespaceHandling = WhitespaceHandling.Significant;
                //project = (Project)serializer.Deserialize(fs);
                reader.Read();
                project.ReadXml(reader);
          //      this.project
                foreach (StoryCard sc in project.Backlog.StoryCardChildren)
                {
                    sc.HandwritingImage = LoadHandWritingImage(sc.ID);
                }

                foreach (Iteration iter in project.IterationChildren)
                {
                    foreach (StoryCard sc in iter.StoryCardChildren)
                    {
                        sc.HandwritingImage = LoadHandWritingImage(sc.ID);
                    }
                }
            }
            finally
            {
                fs.Close();
            }
        }

        /// <summary>
        /// Saves this instance to an xml file.
        /// </summary>
        private void Save()
        {
            lock (project)
            {
                FileStream fs = null;

                try
                {
                    if (projectFile.Exists)
                    {
                        fs = projectFile.Open(FileMode.Truncate, FileAccess.Write);
                    }
                    else
                    {
                        fs = projectFile.Create();
                    }
                    XmlTextWriter writer = new XmlTextWriter(fs, Encoding.UTF8);
                    writer.Flush();
                    project.WriteXml(writer);
                    writer.Flush();

                }
                finally
                {
                    fs.Flush();
                    fs.Close();
                }
            }
        }

        /// <summary>
        /// Saves the hand writing image of a story card if it is different from an already
        /// existing image for that card.
        /// </summary>
        /// <param name="storycard">The storycard.</param>
        private void SaveHandWritingImage(StoryCard storycard)
        {
            if (!handwritingDirectory.Exists)
                handwritingDirectory.Create();

            System.IO.File.WriteAllBytes(handwritingDirectory.FullName + "\\" + storycard.ID.ToString() + ".jpg", storycard.HandwritingImage);
        }

        /// <summary>
        /// Loads the hand writing image for the given card if it exists.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        private byte[] LoadHandWritingImage(long id)
        {
            if (System.IO.File.Exists(handwritingDirectory.FullName + "\\" + id.ToString() + HANDWRITING_FILE_EXTENSION))
                return System.IO.File.ReadAllBytes(handwritingDirectory.FullName + "\\" + id.ToString() + HANDWRITING_FILE_EXTENSION);
            else
                return new byte[0];
        }

        #endregion


        public Project UpdateProject(Project project)
        {
            this.project = project;
            Save();
            return (Project)project.Clone();
        }
    }
}